// pixtek.AutoScroll
// (c) Pixel Technology, August 2001
// info@pixel-technology.com

using System;
using System.Drawing;
using System.Windows.Forms;
using System.ComponentModel;

namespace pixtek
{
	/// <summary>
	/// A control with scale and scroll support
	/// </summary>
	public class AutoScroll : DoubleBufferPaint
	{
		private System.Windows.Forms.HScrollBar hScrollBar;
		private System.Windows.Forms.VScrollBar vScrollBar;
		private pixtek.AutoScale	asX = new pixtek.AutoScale();
		private pixtek.AutoScale	asY = new pixtek.AutoScale();
		private int					hvalue;
		private int					vvalue;
		private bool				bEraseBackground;
		private Rectangle			visibleRectangle;

		/// <summary>
		/// Required designer variable.
		/// </summary>
		private System.ComponentModel.Container components = null;

		public delegate void DoPaintingHandler (Graphics dc, Rectangle clientRectangle, int level);
		public event DoPaintingHandler DoPaintingEvent;

		public delegate void DrawBackgroundHandler (Graphics dc, Rectangle DocArea, Rectangle ViewSize);
		public event DrawBackgroundHandler DrawBackgroundEvent;

		public AutoScroll()
		{
			// This call is required by the Windows.Forms Form Designer.
			InitializeComponent();
			// Set doc size to 1000 for design mode
			asX.DocSize=1000;
			asY.DocSize=1000;
			hvalue=vvalue=0;
			bEraseBackground=true;
		}

		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if( components != null )
					components.Dispose();
			}
			base.Dispose( disposing );
		}

		#region Component Designer generated code
		/// <summary>
		/// Required method for Designer support - do not modify 
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			this.vScrollBar = new System.Windows.Forms.VScrollBar();
			this.hScrollBar = new System.Windows.Forms.HScrollBar();
			this.SuspendLayout();
			// 
			// vScrollBar
			// 
			this.vScrollBar.Dock = System.Windows.Forms.DockStyle.Right;
			this.vScrollBar.Location = new System.Drawing.Point(184, 0);
			this.vScrollBar.Name = "vScrollBar";
			this.vScrollBar.Size = new System.Drawing.Size(16, 184);
			this.vScrollBar.TabIndex = 1;
			this.vScrollBar.ValueChanged += new System.EventHandler(this.vScrollBar_ValueChanged);
			this.vScrollBar.Scroll += new System.Windows.Forms.ScrollEventHandler(this.vScrollBar_Scroll);
			// 
			// hScrollBar
			// 
			this.hScrollBar.Dock = System.Windows.Forms.DockStyle.Bottom;
			this.hScrollBar.Location = new System.Drawing.Point(0, 184);
			this.hScrollBar.Name = "hScrollBar";
			this.hScrollBar.Size = new System.Drawing.Size(200, 16);
			this.hScrollBar.TabIndex = 0;
			this.hScrollBar.ValueChanged += new System.EventHandler(this.hScrollBar_ValueChanged);
			this.hScrollBar.Scroll += new System.Windows.Forms.ScrollEventHandler(this.hScrollBar_Scroll);
			// 
			// AutoScroll
			// 
			this.Controls.AddRange(new System.Windows.Forms.Control[] {
																		  this.vScrollBar,
																		  this.hScrollBar});
			this.Name = "AutoScroll";
			this.Size = new System.Drawing.Size(200, 200);
			this.Paint += new System.Windows.Forms.PaintEventHandler(this.AutoScroll_Paint);
			this.ResumeLayout(false);

		}
		#endregion

		protected virtual void OnDoPainting(Graphics dc, Rectangle clientRectangle, int level)
		{
			if (DoPaintingEvent!=null)
				DoPaintingEvent(dc, clientRectangle, level);
			else
			{
				Pen pen = new Pen(Color.Blue);
				dc.Clear(BackColor);
				dc.DrawLine(pen, clientRectangle.Left, clientRectangle.Top, clientRectangle.Right, clientRectangle.Bottom);
				dc.DrawLine(pen, clientRectangle.Right, clientRectangle.Top, clientRectangle.Left, clientRectangle.Bottom);
			}
		}

		protected virtual void OnDrawBackground (Graphics dc, Rectangle DocArea, Rectangle ViewSize)
		{
			if (DrawBackgroundEvent!=null)
				DrawBackgroundEvent(dc, DocArea, ViewSize);
		}

		public Rectangle VisibleRectangle
		{
			get
			{
				return visibleRectangle;
			}
		}

		public int LargeChange
		{
			get
			{
				return hScrollBar.LargeChange;
			}
			set
			{
				hScrollBar.LargeChange = value;
				vScrollBar.LargeChange = value;
			}
		}

		protected override void DoPainting (Graphics dc, Rectangle clientRectangle, int level)
		{
			int width = ClientRectangle.Width;
			int height = ClientRectangle.Height;
			int	i = 0 ;
			if (hScrollBar.Visible) 
			{
				height-=hScrollBar.Height;
				i++;
			}

			if (vScrollBar.Visible)
			{
				width-=vScrollBar.Width;
				i++;
			}

			dc.Clear(BackColor);

			// Compute de doc size
			visibleRectangle = new Rectangle (hvalue, vvalue, (int)(clientRectangle.Width/asX.Scale), (int)(clientRectangle.Height/asY.Scale));
			OnDrawBackground(dc, visibleRectangle, clientRectangle);

			System.Drawing.Drawing2D.GraphicsContainer container = dc.BeginContainer();
			dc.ScaleTransform(asX.Scale, asY.Scale);
			dc.TranslateTransform(-hvalue, -vvalue, System.Drawing.Drawing2D.MatrixOrder.Append);
			OnDoPainting (dc, clientRectangle, level+1);

			dc.EndContainer(container);

			if (i>0)
			{
				// Paint the right bottom area
				SolidBrush brush = new SolidBrush(BackColor);
				dc.FillRectangle(brush, width, height, vScrollBar.Width, hScrollBar.Height);
				brush.Dispose();
			}

		}

		/// <summary>
		/// Set the correct scale. User must create is own AutoScroll paint event handler
		/// </summary>
		private void AutoScroll_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
		{
			DoPainting(e.Graphics, ClientRectangle, 1);		
		}

        public new void DirectPaint()
        {
            base.DirectPaint();
        }

		protected override void OnPaintBackground(PaintEventArgs e)
		{
			if (bEraseBackground)
				base.OnPaintBackground(e);
		}

		private void _Update()
		{
			int i, fx, fy ;

			// First compute with no scroll bars
			asX.ViewSize=ClientRectangle.Width;
			asY.ViewSize=ClientRectangle.Height;

			// Now see size with scroll bars
			fx=fy=0;
			if(asX.ViewRange!=0 || asY.ViewRange!=0)
			{
				asX.ViewSize=ClientRectangle.Width-vScrollBar.Width;
				asY.ViewSize=ClientRectangle.Height-hScrollBar.Height;
				if (asX.ViewRange>0)
					fy=hScrollBar.Height;
				if (asY.ViewRange>0)
					fx=vScrollBar.Width;
			}

			// Now compute final available area
			asX.ViewSize=ClientRectangle.Width-fx;
			asY.ViewSize=ClientRectangle.Height-fy;

			i=asX.ViewRange;
			if (i>0)
			{
				// Display scrollbar and set range
				hScrollBar.Visible=true;
				hScrollBar.Maximum=i+hScrollBar.LargeChange-1; // TODO: Bug VStudio.NET beta 2
				hScrollBar.Minimum=0;
				hvalue = hScrollBar.Value = Math.Min(hScrollBar.Value, i);
			}
			else
			{
				// Hide scrollbar
				hScrollBar.Visible=false;
				hScrollBar.Value=hvalue=0;
			}

			i=asY.ViewRange;
			if (i>0)
			{
				// Display scrollbar and set range
				vScrollBar.Visible=true;
				vScrollBar.Maximum=i+vScrollBar.LargeChange-1; // Bug VStudio.NET beta 2
				vScrollBar.Minimum=0;
				vvalue = vScrollBar.Value = Math.Min(vScrollBar.Value, i);
			}
			else
			{
				// Hide scrollbar
				vScrollBar.Visible=false;
				vScrollBar.Value=vvalue=0;
			}

			// Check if right and bottom document in view
		}

		private void _Invalidate()
		{
			Rectangle rect = ClientRectangle;
			Size size = new Size(0,0);
			if (vScrollBar.Visible) size.Width-=vScrollBar.Width;
			if (hScrollBar.Visible) size.Height-=hScrollBar.Height;
			DirectPaint(1);
		}

		/// <summary>
		/// Erase background or not
		/// </summary>
		[Description("Erase background or not"),Category("Appearance")]
		public bool EraseBackground
		{
			get { return bEraseBackground; }
			set { bEraseBackground=value; }
		}

		/// <summary>
		/// Set document size
		/// </summary>
		/// <param name="Width"></param>
		/// <param name="Height"></param>
		public void SetDocSize(int Width, int Height)
		{
			asX.DocSize=Width;
			asY.DocSize=Height;
			_Update();
			_Invalidate();
		}

		/// <summary>
		/// Set scale between view and doc
		/// </summary>
		/// <param name="Scale"></param>
		public void SetScale (float Scale)
		{
			asX.Scale=asY.Scale=Scale;
			_Update();
			_Invalidate();
		}

		/// <summary>
		/// Convert view point to document point
		/// </summary>
		/// <param name="ViewPoint">In view range</param>
		/// <returns>A point in document range</returns>
		public Point ViewToDoc (Point ViewPoint)
		{
			return new Point(asX.ViewToDoc(-hvalue, ViewPoint.X),
							 asY.ViewToDoc(-vvalue, ViewPoint.Y));
		}

		/// <summary>
		/// Set top-left document position
		/// </summary>
		/// <param name="ViewPoint">In document range</param>
		public void SetTLDocPosition (Point ViewPoint)
		{
			SetTLDocPosition(ViewPoint.X, ViewPoint.Y);
		}

		/// <summary>
		/// Set top-left document position
		/// </summary>
		/// <param name="X">In document range</param>
		/// <param name="Y">In document range</param>
		public void SetTLDocPosition (int X, int Y)
		{
			int i;
			X = asX.DocToView(X);
			i = asX.ViewRange;
			if (X>i) X=i;
			if (X<0) X=0;
			Y = asY.DocToView(Y);
			i = asY.ViewRange;
			if (Y>i) Y=i;
			if (Y<0) Y=0;
			hScrollBar.Value=X;
			vScrollBar.Value=Y;
		}

		private void hScrollBar_Scroll(object sender, System.Windows.Forms.ScrollEventArgs e)
		{
			hvalue=e.NewValue;
			//_Invalidate();
		}

		private void hScrollBar_ValueChanged(object sender, System.EventArgs e)
		{
			hvalue=hScrollBar.Value;
			_Invalidate();
		}

		private void vScrollBar_Scroll(object sender, System.Windows.Forms.ScrollEventArgs e)
		{
			vvalue=e.NewValue;
			//_Invalidate();
		}

		private void vScrollBar_ValueChanged(object sender, System.EventArgs e)
		{
			vvalue=vScrollBar.Value;
			_Invalidate();
		}

		private void AutoScroll_Resize(object sender, System.EventArgs e)
		{
			_Update();
			_Invalidate();
		}

		private void AutoScroll_SizeChanged(object sender, System.EventArgs e)
		{
			_Update();
			_Invalidate();
		}
	}
}
